using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Threading;


namespace _3D_FileExplorer
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class DrawGraphicsTree : DrawableGameComponent
    {
        private static List<Primitives> cylTree = new List<Primitives>();
        private static List<Primitives> tempCylTree = new List<Primitives>();
        static object x = new object();
        public static CreateGraphicsTree cgt;
        static bool cylTreeReady = false;
        static bool threadIsRunning = false;
        public static bool enableCameraAnimation = false;
        Picking pick;
        static KeyboardControl kControl;
        VertexPositionColorTexture[] rayline = new VertexPositionColorTexture[2];
        int pickupdated = 0;

        static int cylinderPrimitiveCount;
        static int cylinderVertexCount;
        static VertexBuffer cylinderVertexBuffer;
        static IndexBuffer cylinderIndexBuffer;

        static int cubePrimitiveCount;
        static int cubeVertexCount;
        static VertexBuffer cubeVertexBuffer;
        static IndexBuffer cubeIndexBuffer;

        public static Primitives pickedPrimitive;
        SpriteFont font;
        Texture2D texture;
        BoundingFrustum boundingFrustum;
        BasicEffect effect;
        DrawTextLabels TextLabels;
        public static FileSystemDataProvider dp = new FileSystemDataProvider();
        static DrawLeafs leafs;
        float alpha = 1f;
        double previousGameTime;
        static bool leafsVisible = false;
        static bool runOnce = true;
        private static bool levelUpSetCamera = true;
        static string pathTextBoxText = "";
        public static Color color = Color.Black;

        public DrawGraphicsTree(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            effect = Fe.effect;
            boundingFrustum = new BoundingFrustum(Fe.camera.view * Fe.camera.projection);
            pick = new Picking();

            //Leaf component
            base.Initialize();
        }

        protected override void LoadContent()
        {

            // TODO: there will be graphical tree generation code
            effect.VertexColorEnabled = true;
            GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColorTexture.VertexElements);
            cgt = new CreateGraphicsTree(Fe.par.defaultPath, Matrix.Identity, Matrix.Identity, Fe.par.defaultBranchLevel);
            kControl = new KeyboardControl();
            GoTree(cgt.gTree, Matrix.Identity, "", Matrix.Identity);
            cylTreeReady = true;
            CopyCyltree();
            enableCameraAnimation = true;
            LoadBigBuffer(GraphicsDevice); //load data to buffer
            texture = Fe.content.Load<Texture2D>(@"Textures/textura");
            font = Fe.content.Load<SpriteFont>("Fonts/Arial");
            TextLabels = new DrawTextLabels(GraphicsDevice, font, Fe.content);
            base.LoadContent();
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            rayline[0] = new VertexPositionColorTexture(Cursor.cursorRay.Position, Color.Green, new Vector2(0, 0));
            rayline[1] = new VertexPositionColorTexture(Cursor.cursorRay.Direction, Color.Green, new Vector2(0, 0));

            #region NEW THREAD OPERATIONS
            //if ((Mouse.GetState().LeftButton == ButtonState.Pressed)&&(pickedCilinder != null))

            if ((Fe.leftButtonClick) && (pickedPrimitive != null)&&!Fe.par.keyBoardControl)
            {
                Fe.leftButtonClick = false;
                CreateNewThread(GraphicsDevice);
            }
            Fe.leftButtonClick = false;
            if (cylTreeReady == true)
            {
                CopyCyltree();
                LoadBigBuffer(GraphicsDevice); //load data to buffer
                if (Fe.par.keyBoardControl == true) // select first primitive on brach
                {
                    kControl.ResetCounter(0);
                }
                threadIsRunning = false;
            }
            #endregion
            // TODO: Add your update code here
            if (threadIsRunning == false)
            {
                if (Fe.par.keyBoardControl == false)
                {
                    #region PICKING OPERATIONS
                    //update picking every tenth update call (performance optimalization)
                    if (pickupdated == 0)
                    {

                        pickedPrimitive = pick.UpdatePicking(cylTree, Cursor.cursorRay);

                        if (pickedPrimitive != null)
                        {
                            pickedPrimitive.ChangeColor(Color.Yellow);

                        }
                        pickupdated++;
                    }
                    else if (pickupdated == 10)
                    {
                        pickupdated = 0;
                    }
                    else
                    {
                        pickupdated++;
                    }
                    #endregion
                }
                else
                {
                    if (leafsVisible == false)
                    {
                        #region KEYBOARD CONTROL
                        if (runOnce)
                        {
                            runOnce = false;
                            try
                            {
                                pickedPrimitive = cylTree[1];
                            }
                            catch (Exception e)
                            {
                            }
                            pickedPrimitive.ChangeColor(Color.Yellow);
                        }
                        if ((gameTime.TotalGameTime.TotalMilliseconds - previousGameTime) > 150)
                        {
                            previousGameTime = gameTime.TotalGameTime.TotalMilliseconds;
                            if (cgt.gTree[0].Branch.Count > 0)
                            {
                                try
                                {
                                    long pickedNodeId = kControl.Update(cgt.gTree[0]);
                                    if (pickedPrimitive.id != pickedNodeId) //if picked node is changed
                                    {
                                        foreach (Primitives pr in cylTree)
                                        {
                                            if (pickedNodeId == pr.id)
                                            {
                                                pickedPrimitive = pr;
                                                pickedPrimitive.ChangeColor(Color.Yellow);

                                                //show picked object name
                                                break;
                                            }
                                        }
                                    }
                                }
                                catch (Exception e)
                                {
                                    pickedPrimitive = cylTree[1];
                                }
                            }
                            else
                            {
                                kControl.LastBranch();
                            }
                        }
                        if (pickedPrimitive is Cylinder)
                        {
                            Interface.SetMessageBoardPosition3D(pickedPrimitive.vertices[0].Position, Fe.camera, GraphicsDevice);
                            Interface.messageBoard = dp.GetNoDotsShortName(pickedPrimitive.name);

                        }
                        else
                        {
                            if (!leafsVisible)
                            {
                                Interface.SetMessageBoardPosition3D(pickedPrimitive.vertices[0].Position, Fe.camera, GraphicsDevice);
                                Interface.messageBoard = pickedPrimitive.type;
                            }
                        }
                        #endregion
                    }
                }
            }

            //Update bounding frustum
            boundingFrustum.Matrix = Fe.camera.view * Fe.camera.projection;
            if (!Fe.par.keyBoardControl)
            {
                DrawMouseLabel();
            }
            #region CAMERA ANIMATION
            if (Fe.par.enableCameraDollyIn == true)
            {
                if (enableCameraAnimation == true)
                {
                    CameraAnimation ca = new CameraAnimation(Fe.camera, tempCylTree[0].center, tempCylTree[0].boudingSphere.Radius, tempCylTree[0].rotation.Up, Game, true);
                    Game.Components.Add(ca);
                    enableCameraAnimation = false;
                }
            }
            if (levelUpSetCamera)
            {
                CameraAnimation ca = new CameraAnimation(Fe.camera, tempCylTree[0].center, tempCylTree[0].boudingSphere.Radius, tempCylTree[0].rotation.Up, Game, false);
                Game.Components.Add(ca);
                levelUpSetCamera = false;
            }

            #endregion
            Fe.form.ProgressBar.Value = DrawLeafs.progressBarValue;
            base.Update(gameTime);

        }
        public override void Draw(GameTime gameTime)
        {


            //effect.GraphicsDevice.VertexDeclaration = top;
            effect.World = Matrix.Identity;
            effect.View = Fe.camera.view;
            effect.Projection = Fe.camera.projection;
            effect.VertexColorEnabled = true;
            effect.TextureEnabled = true;
            effect.Texture = texture;
            effect.Begin();


            #region ALPHA OPERATIONS
            /*effect.Alpha = alpha;
            if (threadIsRunning == true)
            {
                if (alpha > 0)
                {
                    alpha -= 0.01f;
                }
            }
            else
            {
                if (alpha < 0.99f)
                {
                    alpha += 0.01f;
                }
            }*/
            //GraphicsDevice.RenderState.AlphaBlendEnable = true;
            //GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            #endregion

            #region DRAW CYLINDERS

            effect.DiffuseColor = Fe.par.diffuseColor.ToVector3();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                //Tell the GPU what vertex array to pull vertex data from.

                //Big buffer
                GraphicsDevice.Indices = cylinderIndexBuffer;
                GraphicsDevice.Vertices[0].SetSource(cylinderVertexBuffer, 0, VertexPositionColorTexture.SizeInBytes);
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, cylinderVertexCount, 0, cylinderPrimitiveCount);

                pass.End();
            }

            #endregion

            #region DRAW CUBES
            effect.DiffuseColor = Color.White.ToVector3();
            if (Primitives.bigCubeVertices.Count > 0)
            {
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    //Tell the GPU what vertex array to pull vertex data from.

                    //Big buffer
                    GraphicsDevice.Indices = cubeIndexBuffer;
                    GraphicsDevice.Vertices[0].SetSource(cubeVertexBuffer, 0, VertexPositionColorTexture.SizeInBytes);
                    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, cubeVertexCount, 0, cubePrimitiveCount);
                    pass.End();
                }
            }

            #endregion

            //Picked cylinder
            if (pickedPrimitive != null)
            {
                
                pickedPrimitive.Draw(effect, Fe.camera, GraphicsDevice);
            }

            effect.End();

            #region DRAW_CURSOR_RAY
            /*effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineStrip,rayline, 0, 1);

                pass.End();
            }

            effect.End();*/
            #endregion

            #region DRAW_TEXT_LABELS

            if ((!Fe.par.keyBoardControl) && (!threadIsRunning))
            {
                TextLabels.Draw(Fe.camera, cylTree, boundingFrustum);
            }
            #endregion



            base.Draw(gameTime);
        }

        private void DrawMouseLabel()
        {
            Vector2 mousePosition;
            mousePosition.X = Mouse.GetState().X + 13f;
            mousePosition.Y = Mouse.GetState().Y + 10f;
            if (pickedPrimitive != null)
            {
                if (pickedPrimitive is Cylinder)
                {
                    Interface.messageBoard = dp.GetNoDotsShortName(pickedPrimitive.name);
                    Interface.SetMessageBoardPosition2D(mousePosition);
                }
                else
                {
                    Interface.messageBoard = pickedPrimitive.type;
                    Interface.SetMessageBoardPosition2D(mousePosition);
                }
            }
            else
            {
                Interface.messageBoard = "";
            }

        }

        static void GoTree(List<GraphicalNode> tree, Matrix ancestorsRotation, string ancestorsName, Matrix ancestorsTranslation) //create cilinders
        {
            foreach (GraphicalNode cl in tree)
            {
                if (cl.Dir == true)
                {
                    tempCylTree.Add(new Cylinder(cl.translation, cl.rotation, new Color((byte)(180 * 1 / cl.Weight), (byte)(180 * 1 / cl.Weight), (byte)(180 * 1 / cl.Weight)), cl.Name, cl.ShortName, cl.Type, cl.id, cl.Weight, cl.filesCount, cl.Height, cl.Radius, Fe.par.cylinderFaces, Vector3.Zero, cl.Branchlevel));
                }
                else
                {
                    tempCylTree.Add(new Cube(cl.translation, ancestorsRotation, Color.Gray, cl.Name, cl.ShortName, cl.Type, cl.id, cl.Weight, cl.cubeSide, ancestorsName, ancestorsTranslation));
                }
                if (cl.Branch != null)
                {
                    GoTree(cl.Branch, cl.rotation, cl.Name, cl.translation);
                }
            }
        }


        static void LoadBigBuffer(GraphicsDevice Device)
        {
            //allocate memory in vertex buffer
            //set Cylinder data to vertex buffers
            cylinderVertexBuffer = new VertexBuffer(Device, typeof(VertexPositionColorTexture), Primitives.bigCylinderVertices.Count, BufferUsage.WriteOnly);
            cylinderIndexBuffer = new IndexBuffer(Device, typeof(int), Primitives.bigCylinderIndices.Count, BufferUsage.None);
            cylinderVertexBuffer.SetData(Primitives.bigCylinderVertices.ToArray(), 0, Primitives.bigCylinderVertices.Count);
            cylinderIndexBuffer.SetData(Primitives.bigCylinderIndices.ToArray());
            cylinderPrimitiveCount = Primitives.bigCylinderIndices.Count / 3;
            cylinderVertexCount = Primitives.bigCylinderVertices.Count;


            //set Cube data to vertex buffers
            if (Primitives.bigCubeVertices.Count > 0)
            {
                cubeVertexBuffer = new VertexBuffer(Device, typeof(VertexPositionColorTexture), Primitives.bigCubeVertices.Count, BufferUsage.WriteOnly);
                cubeIndexBuffer = new IndexBuffer(Device, typeof(int), Primitives.bigCubeIndices.Count, BufferUsage.None);
                cubeVertexBuffer.SetData(Primitives.bigCubeVertices.ToArray(), 0, Primitives.bigCubeVertices.Count);
                cubeIndexBuffer.SetData(Primitives.bigCubeIndices.ToArray());
                cubePrimitiveCount = Primitives.bigCubeIndices.Count / 3;
                cubeVertexCount = Primitives.bigCubeVertices.Count;
            }

        }
        public static void CreateNewThread(GraphicsDevice GraphicsDevice)
        {
            //create new thread from picked cylinder
            if ((threadIsRunning == false) && (pickedPrimitive is Cylinder))
            {
                Thread t = new Thread(delegate() { CreateNewTree(pickedPrimitive.name, pickedPrimitive.rotation, pickedPrimitive.translation, Fe.par.defaultBranchLevel, true); });
                threadIsRunning = true;
                t.Name = "new_cilinder";
                t.Start();
                leafsVisible = false;
                Console.WriteLine(t.ThreadState);

            }
            else if ((threadIsRunning == false) && (pickedPrimitive is Cube))
            {

                Thread t = new Thread(delegate() { CreateNewTree(((Cube)pickedPrimitive).ancestorsName, ((Cube)pickedPrimitive).ancestorsRotation, ((Cube)pickedPrimitive).ancestorsTranslation, 1, false); });
                threadIsRunning = true;
                t.Name = "new_cilinder";
                t.Start();
                Console.WriteLine(t.ThreadState);
                try
                {
                    leafs.Dispose();
                }
                catch (Exception e)
                {
                }
                leafs = new DrawLeafs(pickedPrimitive, cylTree[0].name, Program.game);
                leafsVisible = true;
                Interface.messageBoard = "";
                Fe.camera.EnableCameraMovement(false);
                Program.game.Components.Add(leafs);
            }
        }

        public static void CreateNewThread(GraphicsDevice GraphicsDevice, int second) //create new thread by levelUP button
        {
            string name;

            try
            {
                name = dp.GetAncestorsPath(cylTree[0].name);
            }
            catch (Exception e)
            {
                name = cylTree[0].name;
            }


            if (threadIsRunning == false)
            {
                Thread t = new Thread(delegate() { CreateNewTree(name, Matrix.Identity, Matrix.Identity, Fe.par.defaultBranchLevel, true); });
                threadIsRunning = true;
                t.Start();
                leafsVisible = false;
                Console.WriteLine(t.ThreadState);
                levelUpSetCamera = true;
            }
        }

        public static void CreateNewThread(GraphicsDevice GraphicsDevice, string path, int level, bool animateCamera)
        {

            if (threadIsRunning == false)
            {
                Thread t = new Thread(delegate() { CreateNewTree(path, Matrix.Identity, Matrix.Identity, level, animateCamera); });
                threadIsRunning = true;
                t.Start();
                leafsVisible = false;
                Console.WriteLine(t.ThreadState);

            }

        }

        static void CreateNewTree(string path, Matrix rotation, Matrix translation, int level, bool animateCamera)
        {
            pathTextBoxText = path;
            Fe.form.StatusLabel.Text = "Loading data...";
            Primitives.bigCylinderIndices.Clear();
            Primitives.bigCylinderVertices.Clear();
            Primitives.cylinderPrimitivesCount = -1;
            Primitives.bigCubeIndices.Clear();
            Primitives.bigCubeVertices.Clear();
            Primitives.cubePrimitivesCount = -1;
            if (leafs != null)
            {
                //leafs.Dispose();
            }
            //set right rotation
            if (rotation.Up.X == -1f)
            {
                cgt = new CreateGraphicsTree(path, Matrix.CreateRotationZ(-MathHelper.PiOver2) * Matrix.CreateRotationX(MathHelper.PiOver2) * rotation, translation, level);
            }
            if (rotation.Up.X == 1f)
            {
                cgt = new CreateGraphicsTree(path, Matrix.CreateRotationZ(MathHelper.PiOver2) * Matrix.CreateRotationX(MathHelper.PiOver2) * rotation, translation, level);
            }
            if (rotation.Up.Y == -1f)
            {
                cgt = new CreateGraphicsTree(path, Matrix.CreateRotationZ(MathHelper.PiOver2) * Matrix.CreateRotationX(MathHelper.PiOver2) * rotation, translation, level);
            }
            if (rotation.Up.Y == 1f)
            {
                cgt = new CreateGraphicsTree(path, Matrix.CreateRotationZ(-MathHelper.PiOver2) * Matrix.CreateRotationX(MathHelper.PiOver2) * rotation, translation, level);
            }
            if (rotation.Up.Z == -1f)
            {
                cgt = new CreateGraphicsTree(path, Matrix.CreateRotationX(MathHelper.PiOver2) * rotation, translation, level);
            }
            if (rotation.Up.Z == 1f)
            {
                cgt = new CreateGraphicsTree(path, Matrix.CreateRotationZ(MathHelper.Pi) * Matrix.CreateRotationX(MathHelper.PiOver2) * rotation, translation, level);

            }
            // cgt = new CreateGraphicsTree(path, Matrix.CreateRotationX(MathHelper.Pi), translation, level);
            tempCylTree.Clear();
            GoTree(cgt.gTree, Matrix.Identity, "", Matrix.Identity);
            cylTreeReady = true;
            if (animateCamera)
            {
                enableCameraAnimation = true;
            }
            Fe.form.StatusLabel.Text = "";
        }

        private static void CopyCyltree()
        {
            runOnce = true;
            Fe.form.PathTextBox.Text = pathTextBoxText;
            cylTree.Clear();
            kControl.ResetCounter(0);
            cylTree.AddRange(tempCylTree);
            if (pickedPrimitive is Cylinder)
            {
                enableCameraAnimation = true;
            }
            if (leafsVisible == false)
            {
                try
                {
                    Fe.camera.EnableCameraMovement(true);
                    leafs.Dispose();
                }
                catch (Exception e)
                {
                }
            }


            cylTreeReady = false;

        }
    }
}